方法是包含一系列陳述式的程式碼區塊。
程式會造成呼叫方法並指定任何所需的方法引數來執行陳述式。
簡單說方法就是把你本來一長串的程式碼包起來,如果不使用方法的話,可以想像全部東西都塞在 Main 函式裡面,那這個 code 很大機率是難懂難改的。
題外話:個人習慣把有回傳值的 function 稱為方法,無回傳值的 function 稱為函式,只是中文的用詞不同而已,想一想我覺得先說明好了,不然一下方法一下函式,我怕新手會搞不懂,至於回傳值又是什麼後面會解釋。
在介紹方法前,繼續拿 99 乘法表來玩,變形一下讓使用者輸入兩個數字,列印出兩數之間的乘法表,例如輸入 5 和 7 則顯示
5 * 5 = 25 5 * 6 = 30 5 * 7 = 35
6 * 5 = 30 6 * 6 = 36 6 * 7 = 42
7 * 5 = 35 7 * 6 = 42 7 * 7 = 49
先介紹如何在 Console 輸入,首先我們要先去改 launch.json
,因為console
預設是internalConsole
,在 VSCode 看到的 Console 是不能輸入東西的,我們要讓他彈出來,將internalConsole
改成externalTerminal
就可以。
如果拿之前的範例程式執行,會發現彈了一下就消失,因為程式瞬間印出瞬間結束就關掉了,所以我們讓程式執行讀取使用者行為的方法Console.ReadLine()
,這方法可以讀取使用者輸入的文字直到按下 Enter 鍵。
然後我們就可以來撰寫變種 99 乘法表了
Console.WriteLine ("請輸入開始的數字");
int start = int.Parse (Console.ReadLine ());
Console.WriteLine ("請輸入結束的數字");
int end = int.Parse (Console.ReadLine ());
for (int i = start; i <= end; i++) {
for (int j = start; j <= end; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
Console.ReadKey ();
Console.ReadLine()
所接受到的是字串(string),所以需要轉型成 int 型別,int.Parse 方法我會在明天的個人常用方法介紹,除非有下中斷點使程式停住,不然執行完就會直接關閉像我上面說的,所以我在流程結束後加入Console.ReadKey()
,這個方法所接受的是字元(char),等同於按下任意鍵離開。
那如果我們要的是使用者輸入完後,分別印出 1 ~ end、start ~ end、start ~ 9 三種乘法表呢?
如果沒有方法
Console.WriteLine ("請輸入開始的數字");
int start = int.Parse (Console.ReadLine ());
Console.WriteLine ("請輸入結束的數字");
int end = int.Parse (Console.ReadLine ());
for (int i = 1; i <= end; i++) {
for (int j = 1; j <= end; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
for (int i = start; i <= end; i++) {
for (int j = start; j <= end; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
for (int i = start; i <= 9; i++) {
for (int j = start; j <= 9; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
Console.ReadKey ();
經過這麼多次的練習,我們知道 i 與 j 的初始值是開頭的數字,而用來判斷 i 與 j 小於等於的數字是結束的數字,所以如果有個模板是行為一樣,但只替換開頭與結束數字的話,程式碼就會乾淨許多,那就是我們所謂的 function
static void Main (string[] args) {
Console.WriteLine ("請輸入開始的數字");
int start = int.Parse (Console.ReadLine ());
Console.WriteLine ("請輸入結束的數字");
int end = int.Parse (Console.ReadLine ());
MultiplicationTable (1, end);
MultiplicationTable (start, end);
MultiplicationTable (start, 9);
Console.ReadKey ();
}
static void MultiplicationTable (int start, int end) {
for (int i = start; i <= end; i++) {
for (int j = start; j <= end; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
}
是不是簡單許多了?因為本來 C# 是無法在方法裡面寫方法的,所以習慣寫在外面,我會這樣說是因為現在可以了,但我怕會講太遠。我不是很想介紹 C# 成長史,所以現在不會以後也不會特地標明是多少版本之後才能使用,所以有警告訊息時請多多留意,沒意外是不會介紹 C# 7.0 以後的東西,所以範例基本上不會有問題的吧?如果有問題一樣可以留言唷~
可以看到我建立了一個 MultiplicationTable 函式,有型別為 int 變數名為 start 與 end 的變數,這邊我習慣稱為參數(Parameter),關鍵字 static 與回傳值 void 會在後面介紹,並且以括號包住我們的乘法表,所以只要有程式碼呼叫 MultiplicationTable 函式並給予 start 與 end 兩個數字,就可以印出乘法表了。
所以我覺得方法可以拆成幾個重點
一個好的方法名稱,可以讓使用時明確知道方法的用途,命名規則可以參考第二天的參考連結。
建議超過五行的程式碼,只要有被重複使用都應該包成一個方法,並建議依照單一職責原則來撰寫方法,方法的修改只能有一個原因,如果今天因為其他因素而修改該方法,則分離該因素並重建一個方法。
如範例方法的 static,之後會有更詳細的介紹
可以傳入任何東西也可以不傳,可供方法使用該參數內容,在第二天也有說明變數數傳遞時的行為,方法參數關鍵字依照官方的解釋
params 指定這個參數可以接受可變數目的引數。
in 指定這個參數是傳址方式傳遞,但只會由所呼叫的方法讀取。
ref 指定這個參數是傳址方式傳遞,且可以由所呼叫的方法讀取或寫入。
out 指定這個參數是傳址方式傳遞,且由所呼叫的方法寫入。
不是很想寫出方法參數關鍵字的,有點難解釋,後面三個比較廣泛使用,如果後面有提到再特別介紹,不然系列文到今天所學的要介紹完整,不只讀者難懂我也難寫。
參數可以有預設值,當今天其他程式碼呼叫該方法時可以不帶任何引數
static void Main (string[] args) {
MultiplicationTable ();
Console.ReadKey ();
}
static void MultiplicationTable (int start = 5, int end = 8) {
for (int i = start; i <= end; i++) {
for (int j = start; j <= end; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
}
可以看到因為有預設值,所以可以印出從 5 開始到 8 結尾的乘法表。
要注意的是帶有預設值的參數需放在沒有預設值的參數後面,沒有預設值的參數在方法被呼叫時一定要帶入相符合的引數。
可以傳回任何東西也可以不傳回,不傳回時需使用 void 這個關鍵字來指定方法不要傳回值,像我們的乘法表我使用了 void,你也可以傳回一個數字,如一個將數字兩者相加的方法
static void Main (string[] args) {
int number = Add (5, 6);
Console.WriteLine (number);
}
static int Add (int a1, int a2) {
return a1 + a2;
}
跳躍陳述式 return 可以寫在方法任何一個地方,只要方法執行到 return 則會離開方法,即便是 void 的不傳回值也可以用 return 來離開方法,但有傳回值一定要有 return + 正確的傳回型別。
function 內的內容都解釋過一遍了才對啊?為什麼會特別介紹引數呢?引數又是什麼?
引數是將內容丟給方法的變數,所以是在呼叫方法時我們能做一些特別的,那就是具名引數與選擇性引數。
具名引數:如果將參數名稱註明出來便可以無視參數排列規則
static void Main (string[] args) {
MultiplicationTable (end: 5, start: 2);
Console.ReadKey ();
}
static void MultiplicationTable (int start, int end) {
for (int i = start; i <= end; i++) {
for (int j = start; j <= end; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
}
選擇性引數:我認為是結合參數的預設值與具名引數,需帶有預設值的參數才能使用選擇性引數,因為有預設值所以我們可以選擇哪些參數要用具名引數帶入
static void Main (string[] args) {
MultiplicationTable (2, end: 9);
Console.ReadKey ();
}
static void MultiplicationTable (int unused1, int unused2 = 3, int start = 5, int end = 8) {
for (int i = start; i <= end; i++) {
for (int j = start; j <= end; j++) {
Console.Write ($"{i} * {j} = {i * j}\t");
}
Console.Write ("\n");
}
}
方法的參數不一定要有用到才宣告,像上面我多了兩個無用的參數,透過使用選擇性引數,無預設值的參數需放在前面,所以第一個引數 2 為參數 unused1 所用,而具名引數 end 當然是給參數 end 使用。
雖然修飾詞與方法參數關鍵字都沒有說明,但現階段可以算是了解一個 function 的使用方式與時機,還有名詞的差異,如果關於方法有任何問題都可以留言喔~
感謝閱讀。
參考連結
方法 (C# 程式設計手冊) | Microsoft Docs
方法參數 (C# 參考) | Microsoft Docs